//
// Created by long_ on 2021/9/14.
//

#include "plugin_buffer.h"

namespace OHOS {
namespace Multimedia {
namespace Plugin {

Memory::Memory(size_t capacity, std::shared_ptr<uint8_t> bufData, size_t align)
    : capacity(capacity), alignment(align), size(0), allocator(nullptr), addr(std::move(bufData))
{
}

Memory::Memory(size_t capacity, std::shared_ptr<Allocator> allocator, size_t align)
    : capacity(capacity), alignment(align), size(0), allocator(std::move(allocator)), addr(nullptr)
{
    if (this->allocator) {
        addr = std::shared_ptr<uint8_t>(static_cast<uint8_t *>(this->allocator->Alloc(capacity)),
                                        [this](uint8_t *ptr) { this->allocator->Free((void *)ptr); });
    } else {
        addr = std::shared_ptr<uint8_t>(new uint8_t[capacity], std::default_delete<uint8_t[]>());
    }
}

size_t Memory::GetCapacity()
{
    return capacity;
}

void Memory::Reset()
{
    this->size = 0;
}

size_t Memory::Write(const uint8_t *in, size_t writeSize, size_t position) {
    size_t start = 0;
    if (position == std::string::npos) {
        start = size;
    } else {
        start = std::min(position, capacity);
    }

    size_t length = std::min(writeSize, capacity - start);
    std::memcpy(addr.get() + start, in, length);
    size = start + length;
    return length;
}

size_t Memory::Read(uint8_t *out, size_t readSize, size_t position) {
    size_t start = 0;
    if (position != std::string::npos) {
        start = std::min(position, capacity);
    }

    size_t length = std::min(readSize, size);
    std::memcpy(out, addr.get() + start, length);
    return length;
}

const uint8_t* Memory::GetReadOnlyData(size_t position)
{
    if (position > capacity) {
        return nullptr;
    }
    return addr.get() + position;
}

uint8_t *Memory::GetWritableData(size_t writeSize, size_t position)
{
    if (position + writeSize > capacity) {
        return nullptr;
    }
    uint8_t *ptr = addr.get() + position;
    size = (writeSize + position);
    return ptr;
}

size_t Memory::GetSize()
{
    return size;
}

BufferMeta::BufferMeta(BufferMetaType type) : type(type)
{
}

ValueType BufferMeta::GetMeta(Tag tag)
{
    if (tags) {
        return (*tags.get())[tag];
    }
    return new ValueType();
}

void BufferMeta::SetMeta(Tag tag, ValueType value)
{
    if (!tags) {
        tags = std::make_shared<TagMap>();
    }
    (*tags.get())[tag] = value;
}

BufferMetaType BufferMeta::GetType()
{
    return type;
}

Buffer::Buffer(BufferMetaType type)
{
    streamID = 0;
    pts = 0;
    dts = 0;
    duration = 0;
    flag = 0;
    if (type == BufferMetaType::AUDIO) {
        meta = std::shared_ptr<AudioBufferMeta>(new AudioBufferMeta());
    } else if (type == BufferMetaType::VIDEO) {
        meta = std::shared_ptr<VideoBufferMeta>(new VideoBufferMeta());
    }
}

std::shared_ptr<Buffer> Buffer::CreateDefaultBuffer(BufferMetaType type,
        size_t capacity, std::shared_ptr<Allocator> allocator, size_t align)
{
    auto buffer = std::make_shared<Buffer>(type);
    std::shared_ptr<Memory> memory = std::shared_ptr<Memory>(new Memory(capacity, allocator, align));
    buffer->data.push_back(memory);
    return buffer;
}

std::shared_ptr<Memory> Buffer::WrapMemory(uint8_t* data, size_t capacity, size_t size)
{
    auto memory = std::shared_ptr<Memory>(new Memory(capacity, std::shared_ptr<uint8_t>(data, [](void* ptr){})));
    meory->size = size;
    this->data.push_back(memory);
    return memory;
}

std::shared_ptr<Memory> Buffer::WrapMemoryPtr(std::shared_ptr<uint8_t> data, size_t capacity, size_t size)
{
    auto memory = std::shared_ptr<Memory>(new Memory(capacity, data)));
    meory->size = size;
    this->data.push_back(memory);
    return memory;
}

std::shared_ptr<Memory> Buffer::AllocMemory(std::shared_ptr<Allocator> all, size_t capacity, size_t align)
{
    std::shared_ptr<Memory> memory = std::shared_ptr<Memory>(new Memory(capacity, allocator, align));
    this->data.push_back(memory);
    return memory;
}

uint32_t Buffer::GetMemoryCount()
{
    return data.size();
}

std::shared_ptr<Memory> Buffer::GetMemory(uint32_t index)
{
    if (data.size() <= index) {
        return nullptr;
    }
    return data[index];
}

std::shared_ptr<BufferMeta> Buffer::GetBufferMeta()
{
    return meta;
}

bool Buffer::IsEmpty()
{
    return data.empty();
}

void Buffer::Reset() {
    data[0]->Reset();
    streamID = 0;
    pts = 0;
    dts = 0;
    duration = 0;
    flag = 0;
    BufferMetaType type = meta->GetType();
    meta.reset();
    if (type == BufferMetaType::AUDIO) {
        meta = std::shared_ptr<AudioBufferMeta>(new AudioBufferMeta());
    } else if (type == BufferMetaType::VIDEO) {
        meta = std::shared_ptr<VideoBufferMeta>(new VideoBufferMeta());
    }
}

}
}
}